#include <windows.h>
#include <stdio.h>
#include <vector>
#include <common/mapping.h>
#include "fasmsymfmt.h"
#include "converter.h"
#include "helper.h"

// Text strings with FASM symbol type names
static char* szSymTypes[] = {
	"Absolute",
	"Relocatable Segment Address",
	"Relocatable 32-bit Address",
	"Relocatable Relative 32-bit Address",
	"Relocatable 64-bit Address",
	"(ELF) GOT-relative 32-bit Address",
	"(ELF) 32-bit Address of PLT entry",
	"(ELF) Relative 32-bit Address of PLT entry",
};

static void DumpSectionOrExternalSymbol_mini (PFASM_SYMHDR Hdr, FASM_SECTION_OR_EXTERNAL_SYMBOL_REF* Ref)
{
	if (Ref->RelativeToExternal)
	{
		char *Name = FASM_STRING_TABLE(Hdr) + Ref->ExternalSymNameOffset;
		printf("rel: ext_sym '%s' ", Name);
	}
	else
	{
		printf("rel: sect = %d ", Ref->SectionIndex);
		if (Hdr->SectionNames.Length != 0 && Hdr->SectionNames.Offset != 0 && Ref->SectionIndex > 0)
		{
			PDWORD SectNames = FASM_SECTION_NAMES(Hdr);
			char* Name = FASM_STRING_TABLE(Hdr) + SectNames[Ref->SectionIndex-1];
			printf("(%s) ", Name);
		}
	}
}

static PFASM_SYM_LINE DumpLine_Mini (PFASM_SYMHDR Hdr, PFASM_SYM_LINE Line, unsigned iLine);

//
// Mini-version of symbol dumping
//

static void DumpSymbol_Mini (PFASM_SYMHDR Hdr, PFASM_SYMBOL Sym, unsigned iSym)
{
	char* Name = 0;
	bool bFreeName = false;

	if (IS_FASM_SYMBOL_ANONYMOUS(Sym))
		Name = "<Anonymous>";
	else if (IS_FASM_SYMBOL_NAME_IN_PREPROCESSED_SOURCE(Sym))
	{
		PPASCAL_STRING SymName = (PPASCAL_STRING)(FASM_PREPROCESSED_SOURCE(Hdr) + PREPROCESSED_SOURCE_SYMBOL_NAME_OFFSET(Sym));

		Name = PascalToAsciiz(SymName);
		bFreeName = true;
	}
	else if (IS_FASM_SYMBOL_NAME_IN_STRING_TABLE(Sym))
	{
		Name = FASM_STRING_TABLE(Hdr) + STRING_TABLE_SYMBOL_NAME_OFFSET(Sym);
	}

	PFASM_SYM_LINE Line = (PFASM_SYM_LINE)(FASM_PREPROCESSED_SOURCE(Hdr) + Sym->LineOffset);
	if (Sym->LineOffset == 0) Line = NULL;

	char *szSrcFile = 0;

	if (Line != NULL && Line->MacroGenerated == 0)
	{
		if (Line->FileNameOffset == 0)
			szSrcFile = (PSZ)Hdr + Hdr->StringTable.Offset + Hdr->dwInputFileNameOffset;
		else
			szSrcFile = FASM_PREPROCESSED_SOURCE(Hdr) + Line->FileNameOffset;
	}

	printf("#%04x =0x%I64x Type %d (%s), SizeOf: %d, Line: 0x%lx (#%d of %s), ", 
		iSym,
		Sym->Value,
		Sym->Type,
		FASM_SYMBOL_FLAGS_VALID(Sym) ? szSymTypes[Sym->Type] : "<INVALID>",
		Sym->SizeOfData,
		Sym->LineOffset,
		Line == NULL ? -1 : Line->LineNumber,
		szSrcFile == NULL ? "<unk>" : szSrcFile
		);

	DumpSectionOrExternalSymbol_mini (Hdr, &Sym->Ref);

	printf(", Name: '%s'\n", Name);

	if (bFreeName) free (Name);

	if (Line)
		DumpLine_Mini (Hdr, Line, -1);
}

static PFASM_SYM_LINE DumpLine_Mini (PFASM_SYMHDR Hdr, PFASM_SYM_LINE Line, unsigned iLine)
{
	printf(" Line #%d from ", Line->LineNumber);
	printf("%s ", Line->MacroGenerated ? "MACRO" : "FILE");

	// Generated by macro?
	if (Line->MacroGenerated)
	{
		PPASCAL_STRING MacroName = (PPASCAL_STRING)(FASM_PREPROCESSED_SOURCE(Hdr) + Line->MacroNameOffset);
		char* szName = PascalToAsciiz(MacroName);

		printf("%s ", szName);

		free(szName);

		PFASM_SYM_LINE InvokedFrom = (PFASM_SYM_LINE)(FASM_PREPROCESSED_SOURCE(Hdr) + Line->MacroInvokedLineOffset);
		printf("invoked_from_line #%d ", InvokedFrom->LineNumber);

		PFASM_SYM_LINE InMacro = (PFASM_SYM_LINE)(FASM_PREPROCESSED_SOURCE(Hdr) + Line->MacroInvokedLineOffset);
		printf("in_macro_line #%d\n", InMacro->LineNumber);
	}
	else
	{
		PSZ FileName = FASM_PREPROCESSED_SOURCE(Hdr) + Line->FileNameOffset;
		if (Line->FileNameOffset == 0)
			FileName = FASM_STRING_TABLE(Hdr) + Hdr->dwInputFileNameOffset;
		printf("%s ", FileName);
		printf("source_pos %d\n", Line->SourceFilePosition, Line->SourceFilePosition);
	}

	PBYTE pInfo = (PBYTE)(Line+1);

	printf("     ");

	while (pInfo[0] != 0) 
	{
		BYTE Len = pInfo[1];

		switch (pInfo[0])
		{
		case 0x3b:
		case 0x1a:
			{
				char* Str = strndup ((char*)&pInfo[2], Len);

				printf("%s ", Str);
				free (Str);

				pInfo = pInfo + pInfo[1] + 2;
			}
			break;

		case 0x22:
			{
				DWORD dwBytes = *(DWORD*)&pInfo[1];

				char* Value = strndup ((char*)&pInfo[5], dwBytes);

				printf("\"%s\" ", Value);

				free (Value);

				pInfo += sizeof(DWORD) + dwBytes + 1;
				break;
			}
		default:
			printf("%c ", pInfo[0]);
			pInfo ++;
		} // switch()
	}

	printf("\n");

	return (PFASM_SYM_LINE)(++pInfo);
}

typedef struct COFF_SYMS
{
	MEMBLOCK SymTbl;
	MEMBLOCK StrTbl;
} COFF_SYMS, *PCOFF_SYMS;

inline COFF_SYMS CoffSyms (MEMBLOCK a, MEMBLOCK b)
{
	COFF_SYMS cs = {a,b};
	return cs;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////

// #pragma pack (push, 2)
// 
// typedef struct SMART_RELOC
// {
// 	union {
// 		struct {
// 			DWORD  VirtualAddress;
// 			DWORD  SymbolTableIndex;
// 			WORD   Type;
// 		};
// 		IMAGE_RELOCATION r;
// 	};
// 
// 	WORD   _padding1;
// 	PSZ    SymbolName;
// 
// } SMART_RELOC, *PSMART_RELOC;
// 
// #pragma pack (pop)
// 
// struct SECTION_RELOCS
// {
// 	size_t NumberOfRelocations;
// 	//std::vector<SMART_RELOC> Relocs;
// };
// 
// struct COFF_RELOCS
// {
// 	std::vector<SECTION_RELOCS> SectionRelocs;
// };

typedef std::vector<IMAGE_RELOCATION> SECTION_RELOCS;
typedef std::vector<SECTION_RELOCS> COFF_RELOCS;

/*void SaveCoffRelocsNames (PIMAGE_FILE_HEADER FileHdr, COFF_RELOCS &CoffRelocs)
{
	PIMAGE_SECTION_HEADER Sections = (PIMAGE_SECTION_HEADER)(FileHdr + 1);

	PIMAGE_SYMBOL SymTbl = (PIMAGE_SYMBOL)((PCHAR)FileHdr + FileHdr->PointerToSymbolTable);
	PCOFF_STRING_TABLE pST = (PCOFF_STRING_TABLE)(&SymTbl[FileHdr->NumberOfSymbols]);

	CoffRelocs.SectionRelocs.clear();

	for (unsigned i=0; i<FileHdr->NumberOfSections; i++)
	{
		PIMAGE_SECTION_HEADER Section = &Sections[i];
		PIMAGE_RELOCATION Relocs = (PIMAGE_RELOCATION) ((PCHAR)FileHdr + Section->PointerToRelocations);
		SECTION_RELOCS SectionSmartRelocs;

		SectionSmartRelocs.NumberOfRelocations = Section->NumberOfRelocations;

		for (unsigned j=0; j<Section->NumberOfRelocations; j++)
		{
			PIMAGE_RELOCATION Reloc = &Relocs[j];

			SMART_RELOC SmartReloc = {0};
			SmartReloc.r = *Reloc;

			PIMAGE_SYMBOL Sym = &SymTbl[Reloc->SymbolTableIndex];
			SmartReloc.SymbolName = CoffSTGetSymbolName (pST, Sym);

			SectionSmartRelocs.Relocs.push_back (SmartReloc);
		}

		CoffRelocs.SectionRelocs.push_back (SectionSmartRelocs);
	}
}

void RestoreSectionRelocs (PIMAGE_FILE_HEADER FileHdr, COFF_RELOCS &CoffRelocs, HANDLE hFile)
{
	PIMAGE_SECTION_HEADER Sections = (PIMAGE_SECTION_HEADER)(FileHdr + 1);

	PIMAGE_SYMBOL SymTbl = (PIMAGE_SYMBOL)((PCHAR)FileHdr + FileHdr->PointerToSymbolTable);
	PCOFF_STRING_TABLE pST = (PCOFF_STRING_TABLE)(&SymTbl[FileHdr->NumberOfSymbols]);

	for (unsigned i=0; i<FileHdr->NumberOfSections; i++)
	{
		PIMAGE_SECTION_HEADER Section = &Sections[i];

		// Move file pointer to section's relocs
		SetFilePointer (hFile, Section->PointerToRelocations, 0, FILE_BEGIN);

		SECTION_RELOCS* SectionSmartRelocs = &CoffRelocs.SectionRelocs[i];

		for (unsigned j=0; j<Section->NumberOfRelocations; j++)
		{
			SMART_RELOC* Reloc = &SectionSmartRelocs.Relocs[j];

			if (Reloc->SymbolName != NULL)			
			{
				Reloc->SymbolTableIndex = CoffLookupSymbolIndex (
					SymTbl,
					FileHdr->NumberOfSymbols,
					pST,
					Reloc->SymbolName);

				if (Reloc->SymbolTableIndex == -1)
					return printf("Error: can't lookup symbol '%s' while restoring relocs\n", SymbolName);

				FreeSymName (Reloc->SymbolName);
				Reloc->SymbolName = 0;
			}
			else
			{
				Reloc->SymbolTableIndex = -1;
			}

			// Write section relocs
			WriteFile (hFile, &Reloc->r, sizeof(IMAGE_RELOCATION), &wr, 0);

		} // for (relocs)

		SectionSmartRelocs->Relocs.clear();

	} // for (sections)

	CoffRelocs->SectionRelocs.clear();
}*/

void BackupCoffRelocs (PIMAGE_FILE_HEADER FileHdr, COFF_RELOCS &CoffRelocs)
{
	PIMAGE_SECTION_HEADER Sections = (PIMAGE_SECTION_HEADER)(FileHdr + 1);

	PIMAGE_SYMBOL SymTbl = (PIMAGE_SYMBOL)((PCHAR)FileHdr + FileHdr->PointerToSymbolTable);
	PCOFF_STRING_TABLE pST = (PCOFF_STRING_TABLE)(&SymTbl[FileHdr->NumberOfSymbols]);

	CoffRelocs.clear();
	
	for (unsigned i=0; i<FileHdr->NumberOfSections; i++)
	{
		PIMAGE_SECTION_HEADER Section = &Sections[i];
		PIMAGE_RELOCATION Relocs = (PIMAGE_RELOCATION)((PSZ)FileHdr + Section->PointerToRelocations);
		
		SECTION_RELOCS SectRelocs;

		for (unsigned j=0; j<Section->NumberOfRelocations; j++)
		{
			SectRelocs.push_back (Relocs[j]);
		}

		CoffRelocs.push_back (SectRelocs);
	}
}

bool RestoreCoffRelocs (
	HANDLE hFile, 
	PIMAGE_FILE_HEADER FileHdr,
	COFF_RELOCS &CoffRelocs,
	std::vector<IMAGE_SYMBOL> &OrigSymbolTable, 
	PCOFF_STRING_TABLE pOrigST,
	std::vector<IMAGE_SYMBOL> &SymTbl,
	PCOFF_STRING_TABLE pST
	)
{
	PIMAGE_SECTION_HEADER Sections = (PIMAGE_SECTION_HEADER)(FileHdr + 1);

	// For each section
	for (unsigned i=0; i<FileHdr->NumberOfSections; i++)
	{
		PIMAGE_SECTION_HEADER Section = &Sections[i];
		SECTION_RELOCS *SectRelocs = &CoffRelocs[i];  // get relocs

		if (!SectRelocs->empty())
		{
			SetFilePointer (hFile, Section->PointerToRelocations, 0, FILE_BEGIN);

			// For each reloc
			for (unsigned j=0; j<SectRelocs->size(); j++)
			{
				IMAGE_RELOCATION Rel = (*SectRelocs)[j];
				PIMAGE_SYMBOL Sym = &OrigSymbolTable[Rel.SymbolTableIndex];
				PSZ szSrcName = CoffSTGetSymbolName (pOrigST, Sym);
				unsigned iMatchFound = -1;

				if (szSrcName != NULL)
				{
					for (unsigned k=0; k<SymTbl.size() && iMatchFound == -1; k++)
					{
						PIMAGE_SYMBOL TestSym = &SymTbl[k];
						PSZ szTestName = CoffSTGetSymbolName (pST, TestSym);

						if (szTestName)
						{
							if (!strcmp (szTestName, szSrcName))
							{
								printf(" Found match for symbol '%s' : old iSym %d new iSym %d\n", szSrcName, Rel.SymbolTableIndex, k);
								iMatchFound = k;
							}

							FreeSymName (szTestName);

						} // if (reloc has name)

					} // for (new symbols)

					if (iMatchFound == -1)
						printf("Error: can't find match for relocation #%d in section #%d, symbol name: %s\n", j, i, szSrcName);

					FreeSymName (szSrcName);

				} // if (reloc has name)

				if (iMatchFound == -1)
					return false;

				Rel.SymbolTableIndex = iMatchFound;

				WriteBuff (hFile, &Rel, sizeof(IMAGE_RELOCATION));
			
			} // for (relocs)

			SectRelocs->clear();
		
		} // if (!SectRelocs->empty())

	} // for (sections)

	CoffRelocs.clear();

	return true;
}

#define DUPLICATE_PUBLICS 0

COFF_SYMS
CreateCoffSymbolsFromFasmSymVector (
	 IN PFASM_SYMHDR Hdr,
	 IN std::vector<PFASM_SYMBOL> &syms,
	 IN std::vector<IMAGE_SYMBOL> &OldSymbolTable,
	 IN PCOFF_STRING_TABLE OldStringTable,
	 OUT SIZE_T *pcbSyms,
	 IN std::vector<IMAGE_SYMBOL> &newSyms
	)
/*++
	Generates new COFF Symbol Table and String Table.

	Input Parameters:
		Hdr  -  FASM Symbol File (mapped) Header
		syms  - vector with PFASM_SYMBOL pointers to fasm symbols
		OldSymbolTable  -   vector with IMAGE_SYMBOLs from original COFF Symbol Table
		OldStringTable  -   copy of original COFF String Table

	Output Parameters:
		ppCoff - receives pointer to a COFF symbolic information (symtable+strtable)
		pcbCoff - receives size of COFF symbolic information.
		Memory should be freed with free() when is no longer needed.
--*/
{
	size_t cUnique = OldSymbolTable.size();

	size_t cFasm = syms.size();
	size_t cFasmNotUnique = cFasm;

	for (unsigned i=0; i<cFasm; i++) // loop thru all fasm syms
	{
		PFASM_SYMBOL Sym = syms[i];

		if (Sym->SymbolNameOffset == 0)
			cFasmNotUnique--;

#if DUPLICATE_PUBLICS
		if (Sym->Ref.RelativeToExternal == 0)
			cFasmNotUnique++;
#endif
	}

	newSyms.clear();

	size_t cSyms = cUnique + cFasmNotUnique;

	printf("cUnique                  %08x\n", cUnique);
	printf("Unique FASM Symbol Count %08x\n", cFasm);
	printf("Total  FASM Symbol Count %08x\n", cFasmNotUnique);
	printf("cSyms                    %08x\n", cSyms);

	// Prepare symbol table
	//PIMAGE_SYMBOL SymbolTable = new IMAGE_SYMBOL[cSyms];
	//MEMBLOCK SymTbl = {SymbolTable, cSyms*sizeof(IMAGE_SYMBOL)};
	MEMBLOCK SymTbl = AllocateMemBlock (sizeof(IMAGE_SYMBOL) * cSyms);
	PIMAGE_SYMBOL SymbolTable = (PIMAGE_SYMBOL) SymTbl.pBuf;

	memset (SymbolTable, 0, sizeof(IMAGE_SYMBOL) * cSyms);

	size_t cbStringTable = 0;

	PCOFF_STRING_TABLE pUniqueST = STCreatePartialStringTable(OldSymbolTable, OldStringTable);

	printf("Created partial UniqueST %p size 0x%lx\n", pUniqueST, pUniqueST->cbST);

	cbStringTable = STGetStringsLength(pUniqueST);

	//
	// Copy UniqueSymbolTable
	//

	printf("Copying unique symbol records\n");

	for (unsigned i=0; i<cUnique; i++)
	{
		PIMAGE_SYMBOL ImgSym = &OldSymbolTable[i];
		SymbolTable[i] = *ImgSym;
		newSyms.push_back (*ImgSym);
	}


	// This loop generates array of IMAGE_SYMBOLs and calculates 
	//  length for string table.

	printf("Converting FASM symbols\n");

	unsigned iNotUnique = 0;

	for (unsigned i=0; i<cFasm; i++) // loop thru all fasm syms
	{
		PFASM_SYMBOL Sym = syms[i];
		PIMAGE_SYMBOL ImgSym = &SymbolTable[iNotUnique + cUnique];

		// Get FASM symbol name
		// (for extrn-s it returns name of external symbol, for example,
		//  '_MessageBox@16' for MessageBox)
		PSZ szSymName = FasmGetSymbolName(Hdr, Sym);

		if (szSymName == NULL)
		{
			printf(" skipping anonymous symbol\n");
			continue;
		}

		printf(" Converting %s = %I64x\n", szSymName, Sym->Value);

		if (strlen(szSymName) > 8)
		{
			cbStringTable += strlen(szSymName) + 1;
		}
		else
		{
			strncpy ((char*)ImgSym->N.ShortName, szSymName, 8);
		}

		//
		// Here we can add if(!bImgSymCopied) construction
		//  to ensure that IMAGE_SYMBOL for a symbol which existed
		// in the output file symbol table, will not be overwritten.
		// IMAGE_SYMBOL will be filled only for new symbols
		//

		ImgSym->Value = (DWORD) Sym->Value;

		if (Sym->Ref.RelativeToExternal)
		{
			// External
			ImgSym->SectionNumber = IMAGE_SYM_UNDEFINED;
			ImgSym->StorageClass = IMAGE_SYM_CLASS_EXTERNAL;
			ImgSym->Type = IMAGE_SYM_TYPE_NULL;
			iNotUnique ++;
		}
		else
		{
			// Public symbol generate two symbol records.
			// First has type IMAGE_SYM_CLASS_STATIC
			// Second has type IMAGE_SYM_CLASS_EXTERNAL

#if DUPLICATE_PUBLICS
			// First
			ImgSym->SectionNumber = IMAGE_SYM_UNDEFINED; //Sym->Ref.SectionIndex;
			ImgSym->StorageClass = IMAGE_SYM_CLASS_LABEL;
			ImgSym->Type = 0x20;

			printf(" Converting %s = %I64x again\n", szSymName, Sym->Value);

			// Second
			// second should have decorated name!! for successful dependency resolving
			PIMAGE_SYMBOL SymExt = &SymbolTable[iNotUnique + cUnique + 1];
			*SymExt = *ImgSym;
			SymExt->SectionNumber = IMAGE_SYM_UNDEFINED;
			SymExt->StorageClass = IMAGE_SYM_CLASS_EXTERNAL;
			SymExt->Type = IMAGE_SYM_TYPE_NULL;
#else
			ImgSym->SectionNumber = Sym->Ref.SectionIndex;
			ImgSym->StorageClass = IMAGE_SYM_CLASS_EXTERNAL;
			ImgSym->Type = IMAGE_SYM_TYPE_NULL;

			iNotUnique ++;
#endif
		} // external

		FreeSymName (szSymName);
		
	} // for ( .. )

	//
	// Make string table
	//

	printf("Creating new ST.. cbUniqueST = 0x%lx, cbStringTable = 0x%lx\n", STGetStringsLength(pUniqueST), cbStringTable);

	PCOFF_STRING_TABLE pST = STCreate (cbStringTable);

	MEMBLOCK StrTbl = {pST, pST->cbST};

	printf("Created new ST\n");

	// Current pointer in the string table
	char* szCurSTptr = STGetFirstString(pST);

	// Copy UniqueSymbolTable
	memcpy (szCurSTptr, STGetFirstString(pUniqueST), STGetStringsLength(pUniqueST));

	szCurSTptr += STGetStringsLength(pUniqueST);

	printf("Copied unique ST\n");

	printf("Appending FASM symbol names to ST\n");

	iNotUnique = 0;

	// Add symbol names to the string tab\le
	for (unsigned i=0; i<syms.size(); i++) // loop thru all fasm
	{
		PFASM_SYMBOL Sym = syms[i];
		PIMAGE_SYMBOL ImgSym = &SymbolTable[iNotUnique + cUnique];
		
		PSZ szSymName = FasmGetSymbolName (Hdr, Sym);

		if (szSymName == NULL)
		{
			printf(" skipping anonymous symbol\n");
			continue;
		}

		// Is name long?
		if (strlen(szSymName) > 8)
		{
			printf(" appending %s\n", szSymName);

			// Add to string table.
			strcpy (szCurSTptr, szSymName);

			// Calc offset of the symbol name within ST
			DWORD dwOffset = STGetOffset (pST, szCurSTptr);

			// Write to the IMAGE_SYMBOL
			ImgSym->N.Name.Long = dwOffset;
			ImgSym->N.Name.Short = 0;

#if DUPLICATE_PUBLICS
			if (Sym->Ref.RelativeToExternal == 0)
			{
				// For non-external symbols write twice
				PIMAGE_SYMBOL SymExt = &SymbolTable[iNotUnique + cUnique + 1];
				SymExt->N.Name.Long = dwOffset;
				SymExt->N.Name.Short = 0;
			}
#endif

			// Move to the next symbol name
			szCurSTptr += strlen(szCurSTptr) + 1;

			if (szCurSTptr > (char*)pST + pST->cbST)
			{
				printf("Error: ST overflow\n");
				__debugbreak();
			}
		}

#if DUPLICATE_PUBLICS
		if (Sym->Ref.RelativeToExternal == 0)
			iNotUnique += 2;
		else 
#endif
			iNotUnique++;

		// Free symbol name if need
		FreeSymName (szSymName);

		newSyms.push_back (*ImgSym);

	} // for (...)

// 
// 	//
// 	// Allocate memory for the COFF symbols
// 	//
// 
// 	SIZE_T cbCoff = STGetFullLength(pST) + sizeof(IMAGE_SYMBOL)*cSyms;
// 	MEMBLOCK CoffMem = AllocateMemBlock (cbCoff);
// 	PBYTE pCoff = (PBYTE)CoffMem.pBuf;
// 
// 	printf("Allocated memory for COFF symbollic information\n");
// 
// 	// Append symbol table
// 	memcpy (pCoff, SymbolTable, sizeof(IMAGE_SYMBOL)*cSyms);
// 
// 	printf("Copied Symbol Table\n");
// 
// 	// Append string table
// 	STStore (pST, pCoff + sizeof(IMAGE_SYMBOL)*cSyms);
// 
// 	printf("Copied String Table\n");
// 


	// Free temporary symbol table & temporary string table
	STDestroy (pUniqueST);

	printf("Returning pointers\n");

	*pcbSyms = cSyms;

	return CoffSyms (SymTbl, StrTbl);
}

void ExtractCoffSymbolsToLeave (
	std::vector<IMAGE_SYMBOL> &origSyms,
	PCOFF_STRING_TABLE pST,
	std::vector<IMAGE_SYMBOL> &uniqueSyms,
	PFASM_SYMHDR Hdr,
	std::vector<PFASM_SYMBOL> &fasmSymbols
	)
/*++
	Look thru original symbol table and for each entry try to find a match
	 in FASM symbols. If there is no match, add symbol to uniqueSyms.
--*/
{
	for (unsigned i=0; i<origSyms.size(); i++)
	{
		PIMAGE_SYMBOL Sym = &origSyms[i];
		PSZ origSymName = CoffSTGetSymbolName(pST, Sym);

		bool bBreak = false;
		bool bUnique = true;

		for (unsigned k=0; k<fasmSymbols.size() && !bBreak; k++)
		{
			PFASM_SYMBOL FasmSym = fasmSymbols[k];
			PSZ FasmSymName = FasmGetSymbolName (Hdr, FasmSym);
			if (FasmSymName != NULL)
			{
				if (!strcmp (origSymName, FasmSymName))
				{
					// COFF symbol exists in FASM symbol table.
					// Mark as BAD
					bBreak = true;
					bUnique = false;
				}

				FreeSymName (FasmSymName);
			}
		}

		if (bUnique)
		{
			printf("Unique symbol found: %s\n", origSymName);
			uniqueSyms.push_back(*Sym);
		}

		FreeSymName (origSymName);
	}
}

void ExtractSymbolsToSave (PFASM_SYMHDR Hdr, std::vector<PFASM_SYMBOL> &syms)
{
	PFASM_SYMBOL Symbols = FASM_FIRST_SYMBOL(Hdr);
	size_t cSyms = Hdr->SymbolTable.Length / sizeof(FASM_SYMBOL);

	// Enumerate all FASM symbols
	for (unsigned i=0; i<cSyms; i++)
	{
		PFASM_SYMBOL Sym = &Symbols[i];

		if (Sym->LineOffset == 0)
			continue;

		PFASM_SYM_LINE Line = (PFASM_SYM_LINE)(FASM_PREPROCESSED_SOURCE(Hdr) + Sym->LineOffset);

// 		if (Line->FileNameOffset == 0)
// 			DumpSymbol_Mini(Hdr, Sym, i);

		if (Sym->Ref.RelativeToExternal == 1 || 
			(Sym->Ref.RelativeToExternal == 0 && Sym->Ref.SectionIndex > 0))
		{
			//DumpSymbol_Mini(Hdr, Sym, i);

			PSZ szSymName = FasmGetSymbolName (Hdr, Sym);

			if (szSymName != NULL)
			{
				printf("Adding FASM symbol: %s\n", szSymName);

				// Remember FASM symbol
				syms.push_back (Sym);

				FreeSymName (szSymName);
			}
		}
	}
}

void DumpFasmSymsVector (PFASM_SYMHDR Hdr, std::vector<PFASM_SYMBOL> &syms)
{
	for (unsigned i=0; i<syms.size(); i++)
	{
		PFASM_SYMBOL Sym = syms[i];
		PSZ szSymName = FasmGetSymbolName (Hdr, Sym);
		if (szSymName)
		{
			printf(" [%02d] = %I64x = %s\n", i, Sym->Value, szSymName);

			FreeSymName (szSymName);
		}
	}
}

void DumpCoffSymsVector (std::vector<IMAGE_SYMBOL> &syms, PCOFF_STRING_TABLE pST)
{
	for (unsigned i=0; i<syms.size(); i++)
	{
		PIMAGE_SYMBOL Sym = &syms[i];
		PSZ szSymName = CoffSTGetSymbolName (pST, Sym);
		if (szSymName)
		{
			printf(" [%02d] = %08x = %s\n", i, Sym->Value, szSymName);

			FreeSymName (szSymName);
		}
	}
}

//
// Convert FASM Symbol File into MS COFF Symbols and inject them into the specified COFF .obj
//

int FasmSym2CoffObj (char* szSymFile, char* szCoffFile)
{
	SIZE_T Size = 0;
	LPVOID lpSym = MapExistingFile (szSymFile, MAP_READ, 0, &Size);
	if (lpSym == NULL)
		return printf("Error: can't open input file\n");

	std::vector<PFASM_SYMBOL> SymbolsToSave;

	printf("FASM Symbol File mapped at %p size 0x%lx\n", lpSym, Size);
	PFASM_SYMHDR Hdr = (PFASM_SYMHDR) lpSym;
	int res = FasmCheckSymbolsHdr (Hdr, Size);
	if (res != 0)
		return res;

	//
	// Map input obj and extract currect COFF symbols
	//

	// Backups of symbol table and string table from the COFF
	std::vector<IMAGE_SYMBOL> OriginalSymbolTable;
	PCOFF_STRING_TABLE pOriginalST = NULL;

	SIZE_T ObjSize = 0;
	LPVOID lpObj = MapExistingFile (szCoffFile, MAP_READ, 0, &ObjSize);
	if (lpObj == NULL)
		return printf("Error: can't open output file\n");

	printf("Mapped input OBJ at %p size 0x%lx\n", lpObj, ObjSize);

	PIMAGE_FILE_HEADER FileHdr = (PIMAGE_FILE_HEADER)(lpObj);
	printf(" IMAGE_FILE_HEADER\n");
	printf("  PointerToSymbolTable: %08x\n", FileHdr->PointerToSymbolTable);
	printf("  NumberOfSymbols: %08x\n", FileHdr->NumberOfSymbols);
	size_t cbSyms = FileHdr->NumberOfSymbols*sizeof(IMAGE_SYMBOL);
	printf("  NumberOfSymbols*sizeof(IMAGE_SYMBOL) : %08x\n", cbSyms);
	if (FileHdr->NumberOfSymbols && FileHdr->PointerToSymbolTable)
	{
		SIZE_T cbOldST = *(DWORD*)(
			(PCHAR)lpObj
			+ FileHdr->PointerToSymbolTable
			+ cbSyms
			);
		printf("  cbStringTable: %08x\n", cbOldST);
		DWORD dwSymsEndOffset = FileHdr->PointerToSymbolTable
			+ cbSyms
			+ cbOldST;

		printf("  PtrToST + sizeof(IMAGE_SYMBOL)*cSyms + cbStringTable = %08x\n", dwSymsEndOffset);
		printf("  File Size: %08x\n", ObjSize);

		// Does COFF symbols ends at the end of the file?
		if (ObjSize != dwSymsEndOffset)
		{
			return printf("Error: COFF syms placed not at the end of the file\n");
		}
	}
	else
	{
		printf("  WARNING: No COFF Symbol Information in output file\n");
	}

	DWORD OrigPtrToSymTable = 0;
	DWORD OrigNumberOfSyms = 0;

	// Does input COFF have valid COFF Symbols?
	if (FileHdr->NumberOfSymbols && FileHdr->PointerToSymbolTable)
	{
		OrigPtrToSymTable = FileHdr->PointerToSymbolTable;
		OrigNumberOfSyms = FileHdr->NumberOfSymbols;

		PIMAGE_SYMBOL Symbol = (PIMAGE_SYMBOL)((PSZ)lpObj + FileHdr->PointerToSymbolTable);

		PCOFF_STRING_TABLE FileST = (PCOFF_STRING_TABLE)(&Symbol[FileHdr->NumberOfSymbols]);
		SIZE_T cbST = STGetStringsLength (FileST);

		// Backup all symbol records
		for (unsigned i=0; i<FileHdr->NumberOfSymbols; i++,Symbol++)
			OriginalSymbolTable.push_back (*Symbol);

		// Backup string table
		pOriginalST = STDuplicate (FileST);

		printf("Created backup for symbol table and for string table\n");
	}

	//
	// Make backup for backups of all sections
	//

	COFF_RELOCS CoffRelocs;
	BackupCoffRelocs (FileHdr, CoffRelocs);

	// Extract symbols from FASM Symbols File
	ExtractSymbolsToSave (Hdr, SymbolsToSave);

	printf("Extracted FASM symbols to save in OBJ (cSyms %08x)\n", SymbolsToSave.size());
	DumpFasmSymsVector (Hdr, SymbolsToSave);

	// Extract COFF symbols from original table to leave them there.
	std::vector<IMAGE_SYMBOL> UniqueOriginalSymbols;
	ExtractCoffSymbolsToLeave (
		OriginalSymbolTable,
		pOriginalST,
		UniqueOriginalSymbols,
		Hdr,
		SymbolsToSave
		);

	printf("Extracted old unique COFF symbols and remembered (cSyms %08x)\n", UniqueOriginalSymbols.size());
	DumpCoffSymsVector (UniqueOriginalSymbols, pOriginalST);

	// Create new COFF symbols
	SIZE_T cNewSyms = 0;
	std::vector<IMAGE_SYMBOL> newSyms;

	COFF_SYMS CoffSyms = CreateCoffSymbolsFromFasmSymVector (
		Hdr, 
		SymbolsToSave, 
		UniqueOriginalSymbols,
		pOriginalST,
		&cNewSyms,
		newSyms
		);

	printf("COFF syms created, new number of symbols: %d\n", cNewSyms);

	char szOutputFile[MAX_PATH];
	strcpy (szOutputFile, szCoffFile);
	char* pExt = strrchr (szOutputFile, '.');
	if (pExt == NULL)
		strcat (szOutputFile, "_syms.obj");
	else
		strcpy (pExt, "_syms.obj");

	printf("Suggested output file name: %s\n", szOutputFile);

	// Open output for writing
	HANDLE hFile = CreateFile (szOutputFile, GENERIC_WRITE, FILE_SHARE_READ,
		0, CREATE_ALWAYS, 0, 0);
	if (hFile == INVALID_HANDLE_VALUE)
		return printf("Error: can't open output file for writing\n");

	printf("Opened output file for writing\n");

	// Create new file header
	IMAGE_FILE_HEADER NewFileHdr = *FileHdr;
	if (OrigPtrToSymTable == 0 || OrigNumberOfSyms == 0)
	{
		NewFileHdr.PointerToSymbolTable = Size;
		NewFileHdr.NumberOfSymbols = cNewSyms;
		printf("Created output IMAGE_FILE_HEADER::PointerToSymbolTable\n");
	}
	else
	{
		NewFileHdr.NumberOfSymbols = cNewSyms;
		printf("Updated output IMAGE_FILE_HEADER::NumberOfSymbols (%08x)\n", cNewSyms);
	}

	DWORD dwWritten = 0;

	// Write new file header
	//WriteFile (hFile, &NewFileHdr, sizeof(IMAGE_FILE_HEADER), &dwWritten, 0);
	WriteBuff (hFile, &NewFileHdr, sizeof(IMAGE_FILE_HEADER));

	printf("IMAGE_FILE_HEADER written\n");

	size_t cbData = NewFileHdr.PointerToSymbolTable - sizeof(IMAGE_FILE_HEADER);
	void* pData = FileHdr + 1;

	// Write all file contents except symbol table
	WriteBuff (hFile, pData, cbData);

	//
	// Restore COFF Relocs from the backup
	//

	PCOFF_STRING_TABLE pNewST = (PCOFF_STRING_TABLE)(CoffSyms.StrTbl.pBuf);

	bool bOk = RestoreCoffRelocs (
		hFile,
		FileHdr,
		CoffRelocs,
		OriginalSymbolTable,
		pOriginalST,
		newSyms,
		pNewST
		);

	if (!bOk)
		return printf("Error: failed to restore sections' relocs\n");


	// Go to the start of the old COFF symbol table
	SetFilePointer (hFile, NewFileHdr.PointerToSymbolTable, 0, FILE_BEGIN);

	// Write symbol table
	WriteBlock (hFile, CoffSyms.SymTbl);
	
	// Write string table
	WriteBlock (hFile, CoffSyms.StrTbl);

	printf("COFF symbolic information written\n");

	// Close the file
	CloseHandle (hFile);

	// Unmap output file
	UnmapViewOfFile (lpObj);

	printf("%s saved successfully\n", szOutputFile);

	STDestroy (pOriginalST);

	FreeMemBlock (CoffSyms.SymTbl);

	return 0;
}

//
// Convert FASM Symbol File into MS COFF Symbols and inject them into the specified PE EXE
//

int FasmSym2CoffExe (char* szSymFile, char* szExeFile)
{
	printf("Error: not supported\n");
	return -1;
}
